----------------------------------------------------------------------------
-- Domain class
-- @classmod Domain
-- @author v.v.balashoff
-- @license CC BY-NC-SA 4.0
----------------------------------------------------------------------------

local Class = require('leviathan/Class')
local Log = require('leviathan/Log')

local Domain = Class:derive('Domain')

--- Create new domain.
-- Create new instance of domain.
-- @function new
-- @tparam string world Name of world.
-- @tparam string name Name of domain.
-- @tparam string location Name of location.
function Domain:new(world, name, location, view)
    self.id = 'Domain'
    self.entities = {}
    self.components = {}
    self.systems = {}
    self.show = true
    self.enabled = true

    self.name = name
    self.location = location
    self.world = world
    self.changed = true
    self.view = false -- if view = true, this domain will be draw by other domain

    self.buffer =
    {
        worldX = 0,
        worldY = 0,
        w = love.graphics.getWidth(),
        h = love.graphics.getHeight(),
        alpha = 1, -- 0 = transparent, 1 is fully visible (default)
        canvas = false
    }
end

--- Update domain.
-- Update current instance of domain.
-- @function update
-- @tparam int dt Delta-time.
function Domain:update(dt)
    if self.enabled then
        for i = 1, #self.systems do
            local sys = self.systems[i]
            if sys.update then
                sys:update(dt)
            end
        end
        -- update entities coordinates
        local ent = self.entities
        for _, e in pairs(ent) do
            local x, y
            x, y = e:getWorldTransform()
            e.worldX = math.floor(x)
            e.worldY = math.floor(y)
        end
    end
end

--- Draw domain.
-- Draw current instance of domain.
-- @function draw
function Domain:draw()
    if self.enabled then
        for i = 1, #self.systems do
            local sys = self.systems[i]
            if sys.draw then
                self.systems[i]:draw()
            end
        end
    end
end

-- TODO описание
local function priorityCompare(e1, e2)
    return e1.priority < e2.priority
end

-- TODO описание
function Domain:orderEntities()
    table.sort(self.entities, priorityCompare)
end

function Domain:addEntity(e)
    table.insert(self.entities, e)
    self:orderEntities()
end

-- TODO описание
function Domain:addSystem(domain, sys, priority)
    sys.priority = priority or 500
    table.insert(domain.systems, sys)
    table.sort(domain.systems, priorityCompare)
end

-- TODO описание
-- id = string!
function Domain:deleteEntity(id)
    local index, entity
    if not type(entity) == 'string' then
        Log:warning('[Domain] Type of entity must be table! Entity: '..entity.name)
    end
    entity, index = self:getEntity(id)
    table.remove(self.entities, index)
    self:rebuildCache()
end

-- TODO описание
function Domain:getEntity(id)
    local e, index
    for k, v in pairs(self.entities) do
        if v.name == id then
            index = k
            e = v
        end
    end
    return e, index
end

-- TODO описание
function Domain:rebuildCache()
    self.components = {}
    for _, e in pairs(self.entities) do
        for _, c in pairs(e.components) do
            -- adding all components to cache array
            if not self.components[c.typeId] then
                self.components[c.typeId] = {}
            end
            table.insert(self.components[c.typeId], c)
        end
    end
    -- sort all components arrays by priority
    for _, comps in pairs(self.components) do
        table.sort(comps, priorityCompare)
    end
end

-- TODO описание
-- TODO debug only
function Domain:dump()
    print('===========================')
    print('\27[32mDomain: \27[0m'..self.name..' ('..tostring(self)..')')
    print('---------------------------')
    print('  \27[32mEntities:\27[0m')
    print('\27[33m  #', 'prior', 'enabl', 'name', '', '', 'address\27[0m')
    for k, v in pairs(self.entities) do
        print('  '..k, v.priority, v.enabled, v.name, tostring(v))
    end
    print('---------------------------')
    print('  \27[32mComponents:\27[0m')
    for k, v in pairs(self.components) do
        print('  \27[33m[\27[0m'..k..'\27[33m]:\27[0m')
        print('\27[33m', '#', 'prior', 'enabl', 'draw', 'name', '', 'entity', '', 'address\27[0m')
        for n, m in pairs(v) do
            print('', n, m.priority, m.enabled, m.drawable, m.name, '['..m.entity.name..']', tostring(m))
        end
    end
    print('---------------------------')
    print('  \27[32mSystems:\27[0m')
    print('\27[33m  #', 'name\27[0m')
    for k, v in pairs(self.systems) do
        print('  '..k, v.name, v)
    end
--    print('---------------------------')
    print('===========================')
end

return Domain
